<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Deeper Dive into Boost.Asio</title>
<link rel="stylesheet" href="../../../../../../doc/src/boostbook.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.75.2">
<link rel="home" href="../../index.html" title="Chapter&#160;1.&#160;Fiber">
<link rel="up" href="../integration.html" title="Sharing a Thread with Another Main Loop">
<link rel="prev" href="embedded_main_loop.html" title="Embedded Main Loop">
<link rel="next" href="../speculation.html" title="Specualtive execution">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<table cellpadding="2" width="100%"><tr>
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../boost.png"></td>
<td align="center"><a href="../../../../../../index.html">Home</a></td>
<td align="center"><a href="../../../../../../libs/libraries.htm">Libraries</a></td>
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
<td align="center"><a href="../../../../../../more/index.htm">More</a></td>
</tr></table>
<hr>
<div class="spirit-nav">
<a accesskey="p" href="embedded_main_loop.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../integration.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="../speculation.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
</div>
<div class="section">
<div class="titlepage"><div><div><h3 class="title">
<a name="fiber.integration.deeper_dive_into___boost_asio__"></a><a class="link" href="deeper_dive_into___boost_asio__.html" title="Deeper Dive into Boost.Asio">Deeper
      Dive into <a href="http://www.boost.org/doc/libs/release/libs/asio/index.html" target="_top">Boost.Asio</a></a>
</h3></div></div></div>
<p>
        By now the alert reader is thinking: but surely, with Asio in particular,
        we ought to be able to do much better than periodic polling pings!
      </p>
<p>
        This turns out to be surprisingly tricky. We present a possible approach
        in <a href="../../../../examples/asio/round_robin.hpp" target="_top"><code class="computeroutput"><span class="identifier">examples</span><span class="special">/</span><span class="identifier">asio</span><span class="special">/</span><span class="identifier">round_robin</span><span class="special">.</span><span class="identifier">hpp</span></code></a>.
      </p>
<p>
        One consequence of using <a href="http://www.boost.org/doc/libs/release/libs/asio/index.html" target="_top">Boost.Asio</a>
        is that you must always let Asio suspend the running thread. Since Asio is
        aware of pending I/O requests, it can arrange to suspend the thread in such
        a way that the OS will wake it on I/O completion. No one else has sufficient
        knowledge.
      </p>
<p>
        So the fiber scheduler must depend on Asio for suspension and resumption.
        It requires Asio handler calls to wake it.
      </p>
<p>
        One dismaying implication is that we cannot support multiple threads calling
        <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/run.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run</span><span class="special">()</span></code></a>
        on the same <code class="computeroutput"><span class="identifier">io_service</span></code> instance.
        The reason is that Asio provides no way to constrain a particular handler
        to be called only on a specified thread. A fiber scheduler instance is locked
        to a particular thread: that instance cannot manage any other thread&#8217;s fibers.
        Yet if we allow multiple threads to call <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run</span><span class="special">()</span></code>
        on the same <code class="computeroutput"><span class="identifier">io_service</span></code> instance,
        a fiber scheduler which needs to sleep can have no guarantee that it will
        reawaken in a timely manner. It can set an Asio timer, as described above
        &#8212; but that timer&#8217;s handler may well execute on a different thread!
      </p>
<p>
        Another implication is that since an Asio-aware fiber scheduler (not to mention
        <a class="link" href="../callbacks/then_there_s____boost_asio__.html#callbacks_asio"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">yield</span></code></a>)
        depends on handler calls from the <code class="computeroutput"><span class="identifier">io_service</span></code>,
        it is the application&#8217;s responsibility to ensure that <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/stop.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">stop</span><span class="special">()</span></code></a>
        is not called until every fiber has terminated.
      </p>
<p>
        It is easier to reason about the behavior of the presented <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code> scheduler if we require that
        after initial setup, the thread&#8217;s main fiber is the fiber that calls <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run</span><span class="special">()</span></code>,
        so let&#8217;s impose that requirement.
      </p>
<p>
        Naturally, the first thing we must do on each thread using a custom fiber
        scheduler is call <a class="link" href="../fiber_mgmt/fiber.html#use_scheduling_algorithm"><code class="computeroutput">use_scheduling_algorithm()</code></a>. However,
        since <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code> requires an <code class="computeroutput"><span class="identifier">io_service</span></code>
        instance, we must first declare that.
      </p>
<p>
</p>
<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special">&lt;</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span> <span class="special">&gt;</span> <span class="identifier">io_svc</span> <span class="special">=</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">make_shared</span><span class="special">&lt;</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span> <span class="special">&gt;();</span>
<span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">use_scheduling_algorithm</span><span class="special">&lt;</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span> <span class="special">&gt;(</span> <span class="identifier">io_svc</span><span class="special">);</span>
</pre>
<p>
      </p>
<p>
        <code class="computeroutput"><span class="identifier">use_scheduling_algorithm</span><span class="special">()</span></code> instantiates <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code>,
        which naturally calls its constructor:
      </p>
<p>
</p>
<pre class="programlisting"><span class="identifier">round_robin</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special">&lt;</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span> <span class="special">&gt;</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">io_svc</span><span class="special">)</span> <span class="special">:</span>
    <span class="identifier">io_svc_</span><span class="special">(</span> <span class="identifier">io_svc</span><span class="special">),</span>
    <span class="identifier">suspend_timer_</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_svc_</span><span class="special">)</span> <span class="special">{</span>
    <span class="comment">// We use add_service() very deliberately. This will throw</span>
    <span class="comment">// service_already_exists if you pass the same io_service instance to</span>
    <span class="comment">// more than one round_robin instance.</span>
    <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">add_service</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_svc_</span><span class="special">,</span> <span class="keyword">new</span> <span class="identifier">service</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_svc_</span><span class="special">)</span> <span class="special">);</span>
    <span class="identifier">io_svc_</span><span class="special">-&gt;</span><span class="identifier">post</span><span class="special">([</span><span class="keyword">this</span><span class="special">]()</span> <span class="keyword">mutable</span> <span class="special">{</span>
</pre>
<p>
      </p>
<p>
        <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code> binds the passed <code class="computeroutput"><span class="identifier">io_service</span></code> pointer and initializes a <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/steady_timer.html" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">steady_timer</span></code></a>:
      </p>
<p>
</p>
<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special">&lt;</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span> <span class="special">&gt;</span>      <span class="identifier">io_svc_</span><span class="special">;</span>
<span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">steady_timer</span>                       <span class="identifier">suspend_timer_</span><span class="special">;</span>
</pre>
<p>
      </p>
<p>
        Then it calls <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/add_service.html" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">add_service</span><span class="special">()</span></code></a>
        with a nested <code class="computeroutput"><span class="identifier">service</span></code> struct:
      </p>
<p>
</p>
<pre class="programlisting"><span class="keyword">struct</span> <span class="identifier">service</span> <span class="special">:</span> <span class="keyword">public</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">service</span> <span class="special">{</span>
    <span class="keyword">static</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">id</span>                  <span class="identifier">id</span><span class="special">;</span>

    <span class="identifier">std</span><span class="special">::</span><span class="identifier">unique_ptr</span><span class="special">&lt;</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span> <span class="special">&gt;</span>    <span class="identifier">work_</span><span class="special">;</span>

    <span class="identifier">service</span><span class="special">(</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span> <span class="special">&amp;</span> <span class="identifier">io_svc</span><span class="special">)</span> <span class="special">:</span>
        <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">service</span><span class="special">(</span> <span class="identifier">io_svc</span><span class="special">),</span>
        <span class="identifier">work_</span><span class="special">{</span> <span class="keyword">new</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span><span class="special">(</span> <span class="identifier">io_svc</span><span class="special">)</span> <span class="special">}</span> <span class="special">{</span>
    <span class="special">}</span>

    <span class="keyword">virtual</span> <span class="special">~</span><span class="identifier">service</span><span class="special">()</span> <span class="special">{}</span>

    <span class="identifier">service</span><span class="special">(</span> <span class="identifier">service</span> <span class="keyword">const</span><span class="special">&amp;)</span> <span class="special">=</span> <span class="keyword">delete</span><span class="special">;</span>
    <span class="identifier">service</span> <span class="special">&amp;</span> <span class="keyword">operator</span><span class="special">=(</span> <span class="identifier">service</span> <span class="keyword">const</span><span class="special">&amp;)</span> <span class="special">=</span> <span class="keyword">delete</span><span class="special">;</span>

    <span class="keyword">void</span> <span class="identifier">shutdown_service</span><span class="special">()</span> <span class="identifier">override</span> <span class="identifier">final</span> <span class="special">{</span>
        <span class="identifier">work_</span><span class="special">.</span><span class="identifier">reset</span><span class="special">();</span>
    <span class="special">}</span>
<span class="special">};</span>
</pre>
<p>
      </p>
<p>
        ... [asio_rr_service_bottom]
      </p>
<p>
        The <code class="computeroutput"><span class="identifier">service</span></code> struct has a
        couple of roles.
      </p>
<p>
        Its foremost role is to manage a <code class="literal">std::unique_ptr&lt;<a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service__work.html" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span></code></a>&gt;</code>. We want the
        <code class="computeroutput"><span class="identifier">io_service</span></code> instance to continue
        its main loop even when there is no pending Asio I/O.
      </p>
<p>
        But when <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service__service/shutdown_service.html" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">service</span><span class="special">::</span><span class="identifier">shutdown_service</span><span class="special">()</span></code></a>
        is called, we discard the <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span></code>
        instance so the <code class="computeroutput"><span class="identifier">io_service</span></code>
        can shut down properly.
      </p>
<p>
        Its other purpose is to <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/post.html" target="_top"><code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code></a>
        a lambda (not yet shown). Let&#8217;s walk further through the example program before
        coming back to explain that lambda.
      </p>
<p>
        The <code class="computeroutput"><span class="identifier">service</span></code> constructor returns
        to <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code>&#8217;s constructor, which returns
        to <code class="computeroutput"><span class="identifier">use_scheduling_algorithm</span><span class="special">()</span></code>, which returns to the application code.
      </p>
<p>
        Once it has called <code class="computeroutput"><span class="identifier">use_scheduling_algorithm</span><span class="special">()</span></code>, the application may now launch some number
        of fibers:
      </p>
<p>
</p>
<pre class="programlisting"><span class="comment">// server</span>
<span class="identifier">tcp</span><span class="special">::</span><span class="identifier">acceptor</span> <span class="identifier">a</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_svc</span><span class="special">,</span> <span class="identifier">tcp</span><span class="special">::</span><span class="identifier">endpoint</span><span class="special">(</span> <span class="identifier">tcp</span><span class="special">::</span><span class="identifier">v4</span><span class="special">(),</span> <span class="number">9999</span><span class="special">)</span> <span class="special">);</span>
<span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">fiber</span><span class="special">(</span> <span class="identifier">server</span><span class="special">,</span> <span class="identifier">io_svc</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span> <span class="identifier">a</span><span class="special">)</span> <span class="special">).</span><span class="identifier">detach</span><span class="special">();</span>
<span class="comment">// client</span>
<span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="identifier">iterations</span> <span class="special">=</span> <span class="number">2</span><span class="special">;</span>
<span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="identifier">clients</span> <span class="special">=</span> <span class="number">3</span><span class="special">;</span>
<span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">barrier</span> <span class="identifier">b</span><span class="special">(</span> <span class="identifier">clients</span><span class="special">);</span>
<span class="keyword">for</span> <span class="special">(</span> <span class="keyword">unsigned</span> <span class="identifier">i</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span> <span class="identifier">i</span> <span class="special">&lt;</span> <span class="identifier">clients</span><span class="special">;</span> <span class="special">++</span><span class="identifier">i</span><span class="special">)</span> <span class="special">{</span>
    <span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">fiber</span><span class="special">(</span>
            <span class="identifier">client</span><span class="special">,</span> <span class="identifier">io_svc</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span> <span class="identifier">a</span><span class="special">),</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span> <span class="identifier">b</span><span class="special">),</span> <span class="identifier">iterations</span><span class="special">).</span><span class="identifier">detach</span><span class="special">();</span>
<span class="special">}</span>
</pre>
<p>
      </p>
<p>
        Since we don&#8217;t specify a <a class="link" href="../fiber_mgmt.html#class_launch"><code class="computeroutput">launch</code></a>, these fibers are ready to run,
        but have not yet been entered.
      </p>
<p>
        Having set everything up, the application calls <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/run.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run</span><span class="special">()</span></code></a>:
      </p>
<p>
</p>
<pre class="programlisting"><span class="identifier">io_svc</span><span class="special">-&gt;</span><span class="identifier">run</span><span class="special">();</span>
</pre>
<p>
      </p>
<p>
        Now what?
      </p>
<p>
        Because this <code class="computeroutput"><span class="identifier">io_service</span></code> instance
        owns an <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span></code> instance, <code class="computeroutput"><span class="identifier">run</span><span class="special">()</span></code> does not immediately return. But &#8212; none of
        the fibers that will perform actual work has even been entered yet!
      </p>
<p>
        Without that initial <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> call in <code class="computeroutput"><span class="identifier">service</span></code>&#8217;s
        constructor, <span class="emphasis"><em>nothing</em></span> would happen. The application would
        hang right here.
      </p>
<p>
        So, what should the <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> handler execute? Simply <a class="link" href="../fiber_mgmt/this_fiber.html#this_fiber_yield"><code class="computeroutput">this_fiber::yield()</code></a>?
      </p>
<p>
        That would be a promising start. But we have no guarantee that any of the
        other fibers will initiate any Asio operations to keep the ball rolling.
        For all we know, every other fiber could reach a similar <code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">this_fiber</span><span class="special">::</span><span class="identifier">yield</span><span class="special">()</span></code> call first. Control would return to the
        <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code>
        handler, which would return to Asio, and... the application would hang.
      </p>
<p>
        The <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code>
        handler could <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code>
        itself again. But as discussed in <a class="link" href="embedded_main_loop.html#embedded_main_loop">the
        previous section</a>, once there are actual I/O operations in flight &#8212; once
        we reach a state in which no fiber is ready &#8212;
that would cause the thread to
        spin.
      </p>
<p>
        We could, of course, set an Asio timer &#8212; again as <a class="link" href="embedded_main_loop.html#embedded_main_loop">previously
        discussed</a>. But in this <span class="quote">&#8220;<span class="quote">deeper dive,</span>&#8221;</span> we&#8217;re trying to
        do a little better.
      </p>
<p>
        The key to doing better is that since we&#8217;re in a fiber, we can run an actual
        loop &#8212; not just a chain of callbacks. We can wait for <span class="quote">&#8220;<span class="quote">something to happen</span>&#8221;</span>
        by calling <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/run_one.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run_one</span><span class="special">()</span></code></a>
        &#8212; or we can execute already-queued Asio handlers by calling <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/poll.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">poll</span><span class="special">()</span></code></a>.
      </p>
<p>
        Here&#8217;s the body of the lambda passed to the <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> call.
      </p>
<p>
</p>
<pre class="programlisting"> <span class="keyword">while</span> <span class="special">(</span> <span class="special">!</span> <span class="identifier">io_svc_</span><span class="special">-&gt;</span><span class="identifier">stopped</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span>
     <span class="keyword">if</span> <span class="special">(</span> <span class="identifier">has_ready_fibers</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span>
         <span class="comment">// run all pending handlers in round_robin</span>
         <span class="keyword">while</span> <span class="special">(</span> <span class="identifier">io_svc_</span><span class="special">-&gt;</span><span class="identifier">poll</span><span class="special">()</span> <span class="special">);</span>
         <span class="comment">// block this fiber till all pending (ready) fibers are processed</span>
         <span class="comment">// == round_robin::suspend_until() has been called</span>
         <span class="identifier">std</span><span class="special">::</span><span class="identifier">unique_lock</span><span class="special">&lt;</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">mutex</span> <span class="special">&gt;</span> <span class="identifier">lk</span><span class="special">(</span> <span class="identifier">mtx_</span><span class="special">);</span>
         <span class="identifier">cnd_</span><span class="special">.</span><span class="identifier">wait</span><span class="special">(</span> <span class="identifier">lk</span><span class="special">);</span>
     <span class="special">}</span> <span class="keyword">else</span> <span class="special">{</span>
         <span class="comment">// run one handler inside io_service</span>
         <span class="comment">// if no handler available, block this thread</span>
         <span class="keyword">if</span> <span class="special">(</span> <span class="special">!</span> <span class="identifier">io_svc_</span><span class="special">-&gt;</span><span class="identifier">run_one</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span>
             <span class="keyword">break</span><span class="special">;</span>
         <span class="special">}</span>
     <span class="special">}</span>
<span class="special">}</span>
</pre>
<p>
      </p>
<p>
        We want this loop to exit once the <code class="computeroutput"><span class="identifier">io_service</span></code>
        instance has been <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/stopped.html" target="_top"><code class="computeroutput"><span class="identifier">stopped</span><span class="special">()</span></code></a>.
      </p>
<p>
        As long as there are ready fibers, we interleave running ready Asio handlers
        with running ready fibers.
      </p>
<p>
        If there are no ready fibers, we wait by calling <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code>. Once any Asio handler has been called
        &#8212; no matter which &#8212; <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code>
        returns. That handler may have transitioned some fiber to ready state, so
        we loop back to check again.
      </p>
<p>
        (We won&#8217;t describe <code class="computeroutput"><span class="identifier">awakened</span><span class="special">()</span></code>, <code class="computeroutput"><span class="identifier">pick_next</span><span class="special">()</span></code> or <code class="computeroutput"><span class="identifier">has_ready_fibers</span><span class="special">()</span></code>, as these are just like <a class="link" href="../scheduling.html#round_robin_awakened"><code class="computeroutput">round_robin::awakened()</code></a>,
        <a class="link" href="../scheduling.html#round_robin_pick_next"><code class="computeroutput">round_robin::pick_next()</code></a> and <a class="link" href="../scheduling.html#round_robin_has_ready_fibers"><code class="computeroutput">round_robin::has_ready_fibers()</code></a>.)
      </p>
<p>
        That leaves <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> and <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code>.
      </p>
<p>
        Doubtless you have been asking yourself: why are we calling <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run_one</span><span class="special">()</span></code>
        in the lambda loop? Why not call it in <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code>, whose very API was designed for just such
        a purpose?
      </p>
<p>
        Under normal circumstances, when the fiber manager finds no ready fibers,
        it calls <a class="link" href="../scheduling.html#algorithm_suspend_until"><code class="computeroutput">algorithm::suspend_until()</code></a>. Why test <code class="computeroutput"><span class="identifier">has_ready_fibers</span><span class="special">()</span></code>
        in the lambda loop? Why not leverage the normal mechanism?
      </p>
<p>
        The answer is: it matters who&#8217;s asking.
      </p>
<p>
        Consider the lambda loop shown above. The only <span class="bold"><strong>Boost.Fiber</strong></span>
        APIs it engages are <code class="computeroutput"><span class="identifier">has_ready_fibers</span><span class="special">()</span></code> and <a class="link" href="../fiber_mgmt/this_fiber.html#this_fiber_yield"><code class="computeroutput">this_fiber::yield()</code></a>.
        <code class="computeroutput"><span class="identifier">yield</span><span class="special">()</span></code>
        does not <span class="emphasis"><em>block</em></span> the calling fiber: the calling fiber
        does not become unready. It is immediately passed back to <a class="link" href="../scheduling.html#algorithm_awakened"><code class="computeroutput">algorithm::awakened()</code></a>,
        to be resumed in its turn when all other ready fibers have had a chance to
        run. In other words: during a <code class="computeroutput"><span class="identifier">yield</span><span class="special">()</span></code> call, <span class="emphasis"><em>there is always at least
        one ready fiber.</em></span>
      </p>
<p>
        As long as this lambda loop is still running, the fiber manager does not
        call <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code>
        because it always has a fiber ready to run.
      </p>
<p>
        However, the lambda loop <span class="emphasis"><em>itself</em></span> can detect the case
        when no <span class="emphasis"><em>other</em></span> fibers are ready to run: the running fiber
        is not <span class="emphasis"><em>ready</em></span> but <span class="emphasis"><em>running.</em></span>
      </p>
<p>
        That said, <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> and <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code> are in fact called during orderly shutdown
        processing, so let&#8217;s try a plausible implementation.
      </p>
<p>
</p>
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">suspend_until</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">chrono</span><span class="special">::</span><span class="identifier">steady_clock</span><span class="special">::</span><span class="identifier">time_point</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">abs_time</span><span class="special">)</span> <span class="keyword">noexcept</span> <span class="special">{</span>
    <span class="comment">// Set a timer so at least one handler will eventually fire, causing</span>
    <span class="comment">// run_one() to eventually return.</span>
    <span class="keyword">if</span> <span class="special">(</span> <span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">chrono</span><span class="special">::</span><span class="identifier">steady_clock</span><span class="special">::</span><span class="identifier">time_point</span><span class="special">::</span><span class="identifier">max</span><span class="special">)()</span> <span class="special">!=</span> <span class="identifier">abs_time</span><span class="special">)</span> <span class="special">{</span>
        <span class="comment">// Each expires_at(time_point) call cancels any previous pending</span>
        <span class="comment">// call. We could inadvertently spin like this:</span>
        <span class="comment">// dispatcher calls suspend_until() with earliest wake time</span>
        <span class="comment">// suspend_until() sets suspend_timer_</span>
        <span class="comment">// lambda loop calls run_one()</span>
        <span class="comment">// some other asio handler runs before timer expires</span>
        <span class="comment">// run_one() returns to lambda loop</span>
        <span class="comment">// lambda loop yields to dispatcher</span>
        <span class="comment">// dispatcher finds no ready fibers</span>
        <span class="comment">// dispatcher calls suspend_until() with SAME wake time</span>
        <span class="comment">// suspend_until() sets suspend_timer_ to same time, canceling</span>
        <span class="comment">// previous async_wait()</span>
        <span class="comment">// lambda loop calls run_one()</span>
        <span class="comment">// asio calls suspend_timer_ handler with operation_aborted</span>
        <span class="comment">// run_one() returns to lambda loop... etc. etc.</span>
        <span class="comment">// So only actually set the timer when we're passed a DIFFERENT</span>
        <span class="comment">// abs_time value.</span>
        <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">expires_at</span><span class="special">(</span> <span class="identifier">abs_time</span><span class="special">);</span>
        <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">async_wait</span><span class="special">([](</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">error_code</span> <span class="keyword">const</span><span class="special">&amp;){</span>
                                    <span class="identifier">this_fiber</span><span class="special">::</span><span class="identifier">yield</span><span class="special">();</span>
                                  <span class="special">});</span>
    <span class="special">}</span>
    <span class="identifier">cnd_</span><span class="special">.</span><span class="identifier">notify_one</span><span class="special">();</span>
<span class="special">}</span>
</pre>
<p>
      </p>
<p>
        As you might expect, <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> sets an <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/steady_timer.html" target="_top"><code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">steady_timer</span></code></a> to <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/basic_waitable_timer/expires_at.html" target="_top"><code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code></a>
        the passed <a href="http://en.cppreference.com/w/cpp/chrono/steady_clock" target="_top"><code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">chrono</span><span class="special">::</span><span class="identifier">steady_clock</span><span class="special">::</span><span class="identifier">time_point</span></code></a>.
        Usually.
      </p>
<p>
        As indicated in comments, we avoid setting <code class="computeroutput"><span class="identifier">suspend_timer_</span></code>
        multiple times to the <span class="emphasis"><em>same</em></span> <code class="computeroutput"><span class="identifier">time_point</span></code>
        value since every <code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code> call cancels any previous <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/basic_waitable_timer/async_wait.html" target="_top"><code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code></a>
        call. There is a chance that we could spin. Reaching <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> means the fiber manager intends to yield
        the processor to Asio. Cancelling the previous <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code> call would fire its handler, causing <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code>
        to return, potentially causing the fiber manager to call <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> again with the same <code class="computeroutput"><span class="identifier">time_point</span></code>
        value...
      </p>
<p>
        Given that we suspend the thread by calling <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run_one</span><span class="special">()</span></code>, what&#8217;s important is that our <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code>
        call will cause a handler to run, which will cause <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code> to return. It&#8217;s not so important specifically
        what that handler does.
      </p>
<p>
</p>
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">notify</span><span class="special">()</span> <span class="keyword">noexcept</span> <span class="special">{</span>
    <span class="comment">// Something has happened that should wake one or more fibers BEFORE</span>
    <span class="comment">// suspend_timer_ expires. Reset the timer to cause it to fire</span>
    <span class="comment">// immediately, causing the run_one() call to return. In theory we</span>
    <span class="comment">// could use cancel() because we don't care whether suspend_timer_'s</span>
    <span class="comment">// handler is called with operation_aborted or success. However --</span>
    <span class="comment">// cancel() doesn't change the expiration time, and we use</span>
    <span class="comment">// suspend_timer_'s expiration time to decide whether it's already</span>
    <span class="comment">// set. If suspend_until() set some specific wake time, then notify()</span>
    <span class="comment">// canceled it, then suspend_until() was called again with the same</span>
    <span class="comment">// wake time, it would match suspend_timer_'s expiration time and we'd</span>
    <span class="comment">// refrain from setting the timer. So instead of simply calling</span>
    <span class="comment">// cancel(), reset the timer, which cancels the pending sleep AND sets</span>
    <span class="comment">// a new expiration time. This will cause us to spin the loop twice --</span>
    <span class="comment">// once for the operation_aborted handler, once for timer expiration</span>
    <span class="comment">// -- but that shouldn't be a big problem.</span>
    <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">async_wait</span><span class="special">([](</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">error_code</span> <span class="keyword">const</span><span class="special">&amp;){</span>
                                <span class="identifier">this_fiber</span><span class="special">::</span><span class="identifier">yield</span><span class="special">();</span>
                              <span class="special">});</span>
    <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">expires_at</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">chrono</span><span class="special">::</span><span class="identifier">steady_clock</span><span class="special">::</span><span class="identifier">now</span><span class="special">()</span> <span class="special">);</span>
<span class="special">}</span>
</pre>
<p>
      </p>
<p>
        Since an <code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code>
        call cancels any previous <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code> call, we can make <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code> simply call <code class="computeroutput"><span class="identifier">steady_timer</span><span class="special">::</span><span class="identifier">expires_at</span><span class="special">()</span></code>. That should cause the <code class="computeroutput"><span class="identifier">io_service</span></code>
        to call the <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code>
        handler with <code class="computeroutput"><span class="identifier">operation_aborted</span></code>.
      </p>
<p>
        The comments in <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code>
        explain why we call <code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code> rather than <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/basic_waitable_timer/cancel.html" target="_top"><code class="computeroutput"><span class="identifier">cancel</span><span class="special">()</span></code></a>.
      </p>
<p>
        This <code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code> implementation is used in
        <a href="../../../../examples/asio/autoecho.cpp" target="_top"><code class="computeroutput"><span class="identifier">examples</span><span class="special">/</span><span class="identifier">asio</span><span class="special">/</span><span class="identifier">autoecho</span><span class="special">.</span><span class="identifier">cpp</span></code></a>.
      </p>
<p>
        It seems possible that you could put together a more elegant Fiber / Asio
        integration. But as noted at the outset: it&#8217;s tricky.
      </p>
</div>
<table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
<td align="left"></td>
<td align="right"><div class="copyright-footer">Copyright &#169; 2013 Oliver Kowalke<p>
        Distributed under the Boost Software License, Version 1.0. (See accompanying
        file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
      </p>
</div></td>
</tr></table>
<hr>
<div class="spirit-nav">
<a accesskey="p" href="embedded_main_loop.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../integration.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="../speculation.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
</div>
</body>
</html>
